package org.ricks.ioc.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.*;
import java.util.function.Predicate;

public class AnnotationUtils {


    /**
     * 获取元素上直接存在的指定注解，或递归查找被该注解修饰的元注解
     *
     * @param element        注解元素（类/方法/字段等）
     * @param annotationType 目标注解类型
     * @return 找到的注解实例，优先返回直接注解
     */
    public static Annotation getAnnotation(AnnotatedElement element, Class<? extends Annotation> annotationType) {
        // 优先查找直接注解
        Annotation directAnnotation = element.getAnnotation(annotationType);
        if (directAnnotation != null) {
            return directAnnotation;
        }

        // 递归查找元注解
        return Arrays.stream(element.getAnnotations())
                .flatMap(metaAnn -> findMetaAnnotations(metaAnn.annotationType(), annotationType).stream())
                .findFirst()
                .orElse(null);
    }

    private static List<Annotation> findMetaAnnotations(
            Class<? extends Annotation> annotationType,
            Class<? extends Annotation> targetType
    ) {
        List<Annotation> results = new ArrayList<>();
        // 记录已访问的注解类型，避免循环引用
        Set<Class<? extends Annotation>> visited = new HashSet<>();
        // 使用栈替代递归（深度优先搜索）
        Deque<Class<? extends Annotation>> stack = new ArrayDeque<>();

        stack.push(annotationType);
        visited.add(annotationType);

        while (!stack.isEmpty()) {
            Class<? extends Annotation> current = stack.pop();
            for (Annotation metaAnn : current.getAnnotations()) {
                Class<? extends Annotation> metaType = metaAnn.annotationType();

                // 跳过已处理的注解类型（防循环）
                if (!visited.add(metaType)) continue;

                if (metaType == targetType) {
                    results.add(metaAnn);
                } else {
                    stack.push(metaType); // 继续搜索元注解
                }
            }
        }
        return results;
    }


    // 示例：增加属性过滤
    public static Optional<Annotation> findAnnotationWithCondition(
            AnnotatedElement element,
            Class<? extends Annotation> targetType,
            Predicate<Annotation> condition) {
        return Arrays.stream(element.getAnnotations())
                .filter(ann -> ann.annotationType() == targetType && condition.test(ann))
                .findFirst()
                .or(() -> Arrays.stream(element.getAnnotations())
                        .flatMap(metaAnn -> findMetaAnnotations(metaAnn.annotationType(), targetType).stream())
                        .filter(condition)
                        .findFirst());
    }

}
